﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using PS.Practices.Provisioner;
#if MOSS
using Microsoft.SharePoint.Publishing;
#endif
using SysWebPart = System.Web.UI.WebControls.WebParts.WebPart;
using PS.Practices.Util;

namespace PS.Practices.SiteBuilder.Features.Schema.DOM
{
    public class FixupFiles : WebOperation
    {
        private List<File> files = new List<File>();

        [XmlElement("File")]
        public List<File> Files
        {
            get { return files; }
            set { files = value; }
        }

        public void Run(SPWeb web)
        {
            foreach (File file in files)
            {
                file.Fixup(web);
            }
        }

        public override void Execute(SPWeb web)
        {
#if SANDBOX
            Logger.Info("'FixupFiles' operation is not available within the sandbox");
            return;
#endif
            Run(web);
        }
    }

    public class File
    {
        private string path;
        private List<WebPart> webParts = new List<WebPart>();
        private List<DataViewsZone> dataViewsZone = new List<DataViewsZone>();

        [XmlAttribute]
        public string Path
        {
            get { return path; }
            set { path = value; }
        }

        [XmlElement("WebPart")]
        public List<WebPart> WebParts
        {
            get { return webParts; }
            set { webParts = value; }
        }

        [XmlElement(typeof(DataViewsInZone))]
        [XmlElement(typeof(DataViewsOutZone))]
        public List<DataViewsZone> DataViewsZone
        {
            get { return dataViewsZone; }
            set { dataViewsZone = value; }
        }

        public void Fixup(SPWeb web)
        {
            SPFile spFile = web.GetFile(this.Path);

            SPListItem item = null;
            try
            {
                item = spFile.Item;
            }
            catch (SPException)
            {
            }
#if MOSS
            PublishingPage page = null;

            if (item != null)
            {
                if (PublishingPage.IsPublishingPage(item))
                {
                    page = PublishingPage.GetPublishingPage(item);
                    if (page.ListItem.File.CheckOutStatus == SPFile.SPCheckOutStatus.None)
                    {
                        page.CheckOut();
                        page.Update();
                    }
                }

                else
                {
                    try
                    {
                        spFile.CheckOut();
                        spFile.Update();
                    }
                    catch (SPException)
                    {
                    }
                }
            }
#else

            try
            {
                spFile.CheckOut();
                spFile.Update();
            }
            catch (SPException)
            {
            }

#endif

            using (SPLimitedWebPartManager manager =
                spFile.GetLimitedWebPartManager(PersonalizationScope.Shared))
            {
                SPLimitedWebPartCollection pageWebParts = manager.WebParts;
                if (pageWebParts == null)
                {
#if MOSS
                    if (page != null)
                        page.CheckIn("");
#endif
                    return;
                }

                foreach (WebPart webPart in this.WebParts)
                {
                    SysWebPart pageWebPart;
                    if (FindWebPart(pageWebParts, webPart, out pageWebPart))
                    {
                        bool saveChanges = webPart.Process(pageWebPart, web, manager);
                        if (saveChanges)
                        {
                            try
                            {
                                manager.SaveChanges(pageWebPart);
                            }
                            catch (WebPartPageUserException)
                            {
                            }
                        }
                    }
                }

                foreach (DataViewsZone viewsZone in dataViewsZone)
                {
                    viewsZone.Process(web, spFile);
                }
            }

            const string txt = "Post provisioning modifications";
#if MOSS
            if (page != null)
            {
                string comment = txt;
                page.CheckIn(comment);
                spFile.Publish(comment);

                try
                {
                    spFile.Approve(comment);
                }
                catch (SPException)
                {
                }
            }
            else
#endif
            {
                if (item != null)
                {
                    try
                    {
                        spFile.CheckIn(txt);
                        spFile.Publish(txt);
                        spFile.Approve(txt);
                    }
                    catch (SPException)
                    {
                    }
                }
            }
        }

        private static bool FindWebPart(SPLimitedWebPartCollection webParts, WebPart webPart,
            out SysWebPart pageWebPart)
        {
            bool res = false;
            pageWebPart = null;
            foreach (SysWebPart part in webParts)
            {
                if (string.Compare(part.Title, webPart.Title, true) == 0)
                {
                    res = true;
                    pageWebPart = part;
                    break;
                }
            }
            return res;
        }
    }

    public class WebPart
    {
        private string title;
        //private UpdateWebPartTitle updateTitle;
        //private SetListViewAndToolbar setListViewAndToolbar;
        //private SetProperty setProperty;
        private List<WebpartOperation> operations;

        [XmlAttribute]
        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        //[XmlElement("UpdateTitle")]
        //public UpdateWebPartTitle UpdateTitle
        //{
        //    get { return updateTitle; }
        //    set { updateTitle = value; }
        //}

        //[XmlElement]
        //public SetProperty SetProperty
        //{
        //    get { return setProperty; }
        //    set { setProperty = value; }
        //}

        //[XmlElement("SetListViewAndToolbar")]
        //public SetListViewAndToolbar SetListViewAndToolbar
        //{
        //    get { return setListViewAndToolbar; }
        //    set { setListViewAndToolbar = value; }
        //}

        [XmlElement(typeof(SetListViewAndToolbar))]
        [XmlElement(typeof(UpdateWebPartTitle))]
        [XmlElement(typeof(SetProperty))]
        public List<WebpartOperation> Operations
        {
            get { return operations; }
            set { operations = value; }
        }

        public bool Process(SysWebPart webPart, SPWeb web, SPLimitedWebPartManager manager)
        {
            //if (this.SetProperty != null)
            //{
            //    this.SetProperty.Process(webPart, web, manager);
            //}

            //if (this.SetListViewAndToolbar != null)
            //{
            //    this.SetListViewAndToolbar.Process(webPart, web, manager);
            //}

            //if (this.UpdateTitle != null)
            //{
            //    this.UpdateTitle.Process(webPart, web, manager);
            //}

            foreach (WebpartOperation operation in operations)
            {
                operation.Process(webPart, web, manager);
            }

            return true;
        }


    }

    public abstract class WebpartOperation
    {
        public abstract void Process(SysWebPart webPart, SPWeb web, SPLimitedWebPartManager manager);
    }

    public class SetListViewAndToolbar : WebpartOperation
    {
        private string view;
        private string list;
        private string toolbar;
        private string listUrl;

        [XmlAttribute]
        public string View
        {
            get { return view; }
            set { view = value; }
        }

        [XmlAttribute]
        public string List
        {
            get { return list; }
            set { list = value; }
        }

        [XmlAttribute]
        public string Toolbar
        {
            get { return toolbar; }
            set { toolbar = value; }
        }

        [XmlAttribute]
        public string ListUrl
        {
            get { return listUrl; }
            set { listUrl = value; }
        }

        public override void Process(SysWebPart webPart, SPWeb web, SPLimitedWebPartManager manager)
        {
            ListViewWebPart lvwp = webPart as ListViewWebPart;
            if (lvwp == null)
            {
                Logger.Info("WebPart is not of ListViewWebPart type: " + webPart.Title);
                return;
            }

            SPList list = null;
            if (!string.IsNullOrEmpty(this.ListUrl))
            {
                try
                {
                    list = web.GetList(this.ListUrl);
                }
                catch (SPException)
                {
                }
            }

            if (list == null)
            {
                if (string.IsNullOrEmpty(this.List))
                {
                    try
                    {
                        Guid listGuid = new Guid(lvwp.ListName);
                        list = web.Lists[listGuid];
                    }
                    catch (FormatException)
                    {
                    }
                    catch (SPException)
                    {
                    }
                }
                else
                {
                    try
                    {
                        list = web.Lists[this.List];
                    }
                    catch (SPException)
                    {
                    }
                }
            }

            if (list == null)
            {
                Logger.Info("Could not resolve list from web: " + web.Title);
                return;
            }

            SPView view;
            if (!string.IsNullOrEmpty(this.View))
            {
                view = list.Views[this.View];

            }
            else
            {
                view = list.Views[new Guid(lvwp.ViewGuid)];
            }

            uint toolbar = 0;
            string strToolbar = this.Toolbar;
            if (!string.IsNullOrEmpty(strToolbar))
            {
                if (!uint.TryParse(strToolbar, out toolbar))
                {
                    switch (strToolbar)
                    {
                        case "Standard":
                            toolbar = 0;
                            break;
                        case "Freeform":
                            toolbar = 1;
                            break;
                        default:
                            toolbar = 2;
                            break;
                    }
                }
            }
            WebPartUtil.SetListViewWebPartView(lvwp, view, toolbar, manager);
        }
    }

    public class UpdateWebPartTitle : WebpartOperation
    {
        private string set;

        [XmlAttribute]
        public string Set
        {
            get { return set; }
            set { set = value; }
        }

        public override void Process(SysWebPart webPart, SPWeb web, SPLimitedWebPartManager manager)
        {
            //Warning: This is not precise code conversion, needs to check
            ParserUtil.UpdateWPTitle(webPart, web, this.Set, null);
        }
    }

    public class SetProperty : WebpartOperation
    {
        private string name;
        private string value;

        [XmlAttribute]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        [XmlAttribute]
        public string Value
        {
            get { return value; }
            set { this.value = value; }
        }

        public override void Process(SysWebPart webPart, SPWeb web, SPLimitedWebPartManager manager)
        {
            if (!string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Value))
            {
                WebPartUtil.SetWPProperty(webPart, Name, Value, web);
            }
        }
    }

    public abstract class DataViewsZone
    {
        private ListInstance[] listInstances;

        [XmlArray("ListInstances")]
        public ListInstance[] ListInstances
        {
            get { return listInstances; }
            set { listInstances = value; }
        }

        public abstract void Process(SPWeb web, SPFile spFile);
    }

    public class DataViewsInZone : DataViewsZone
    {
        public override void Process(SPWeb web, SPFile spFile)
        {
            using (SPLimitedWebPartManager manager =
                spFile.GetLimitedWebPartManager(PersonalizationScope.Shared))
            {
                SPLimitedWebPartCollection pageWebParts = manager.WebParts;

                foreach (Microsoft.SharePoint.WebPartPages.WebPart webPart in pageWebParts)
                {
                    DataFormWebPart dataForm = webPart as DataFormWebPart;
                    if (dataForm == null)
                    {
                        continue;
                    }

                    SubstituteGuidInZone(web, manager, dataForm);
                }
            }
        }

        private void SubstituteGuidInZone(SPWeb web, SPLimitedWebPartManager manager, DataFormWebPart dataForm)
        {
            foreach (ListInstance listInstance in this.ListInstances)
            {
                if (string.IsNullOrEmpty(listInstance.ID) || string.IsNullOrEmpty(listInstance.Title))
                {
                    continue;
                }

                SPList list;
                try
                {
                    list = web.Lists[listInstance.Title];
                }
                catch (ArgumentException)
                {
                    continue;
                }

                if (list == null)
                {
                    continue;
                }
                string newId = list.ID.ToString();

                dataForm.ParameterBindings = Regex.Replace(dataForm.ParameterBindings, listInstance.ID, newId, RegexOptions.IgnoreCase);
                dataForm.DataSourcesString = Regex.Replace(dataForm.DataSourcesString, listInstance.ID, newId, RegexOptions.IgnoreCase);
                dataForm.Xsl = Regex.Replace(dataForm.Xsl, listInstance.ID, newId, RegexOptions.IgnoreCase);

                manager.SaveChanges(dataForm);

            };
        }
    }

    public class DataViewsOutZone : DataViewsZone
    {
        public override void Process(SPWeb web, SPFile file)
        {
            string fileName = file.Name;

            string content;
            using (StreamReader reader = new StreamReader(file.OpenBinaryStream()))
            {
                content = reader.ReadToEnd();
                content = SubstituteGuid(web, content);
            }

            UTF8Encoding encoder = new UTF8Encoding();
            byte[] contentAsBytes = encoder.GetBytes(content);

            // store to temporary file
            SPFile temp = web.Files.Add("temp.aspx", contentAsBytes);

            SPLimitedWebPartManager sourceManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
            SPLimitedWebPartCollection sourceWebParts = sourceManager.WebParts;

            SPLimitedWebPartManager targetManager = temp.GetLimitedWebPartManager(PersonalizationScope.Shared);
            foreach (SysWebPart webPart in sourceWebParts)
            {
                string zoneId = sourceManager.GetZoneID(webPart);
                targetManager.AddWebPart(webPart, zoneId, webPart.ZoneIndex);
            }

            foreach (SPWebPartConnection connection in sourceManager.SPWebPartConnections)
            {
                targetManager.SPConnectWebParts(connection.Provider, connection.ProviderConnectionPoint, connection.Consumer, connection.ConsumerConnectionPoint, connection.Transformer);
            }

            //Dispose/Close was not called on SPLimitedWebPartManager.Web
            sourceManager.Web.Dispose();

            file.Delete();

            temp.CopyTo(fileName);
            temp.Delete();

            web.Update();
        }

        private string SubstituteGuid(SPWeb web, string content)
        {
            string correctContent = content;
            foreach (ListInstance listInstance in this.ListInstances)
            {
                if (string.IsNullOrEmpty(listInstance.ID) || string.IsNullOrEmpty(listInstance.Title))
                {
                    continue;
                }

                SPList list;
                try
                {
                    list = web.Lists[listInstance.Title];
                }
                catch (ArgumentException)
                {
                    continue;
                }

                if (list == null)
                {
                    return String.Empty;
                }
                string newId = list.ID.ToString();

                correctContent = Regex.Replace(correctContent, listInstance.ID, newId, RegexOptions.IgnoreCase);
            }

            return correctContent;
        }
    }

    public class ListInstance
    {
        private string id;
        private string title;

        [XmlAttribute]
        public string ID
        {
            get { return id; }
            set { id = value; }
        }

        [XmlAttribute]
        public string Title
        {
            get { return title; }
            set { title = value; }
        }
    }
}
